using System;
using System.Collections.Generic;
using System.Text;
using System.Workflow.Activities;
using System.Workflow.ComponentModel;
using Microsoft.Crm.Workflow;
using Microsoft.Crm.Sdk;
using System.Diagnostics;
using Microsoft.Crm.SdkTypeProxy;
using Microsoft.Crm.Sdk.Query;
using Microsoft.Crm.Sdk.Metadata;
using Microsoft.Crm.SdkTypeProxy.Metadata;

namespace Infusion.CRM.WF.CRM40DistributeWF
{

    /// <summary>
    /// Execute a specified workflow for all objects in a child relationship.
    /// </summary>
    [CrmWorkflowActivity("Distribute Workflow (1:N)", "Utilities")]
    public class DistributeWFActivityOnetoMany : SequenceActivityBase
    {

        #region Workflow Parameters

        /// <summary>
        /// Input parameter: Workflow to be executed.
        /// </summary> 
        public static DependencyProperty WorkflowProperty = DependencyProperty.Register("Workflow", typeof(Lookup), typeof(DistributeWFActivityOnetoMany));
        [CrmInput("Distributed Workflow")]
        [CrmReferenceTarget("workflow")]
        public Lookup Workflow
        {
            get
            {
                return (Lookup)base.GetValue(WorkflowProperty);
            }
            set
            {
                base.SetValue(WorkflowProperty, value);
            }
        }

        /// <summary>
        /// Name of the relationship as specified in the customization corresponding field.
        /// </summary>
        public static DependencyProperty RelationshipNameProperty = DependencyProperty.Register("RelationshipName", typeof(string), typeof(DistributeWFActivityOnetoMany));
        [CrmInput("Relationship Name")]
        public string RelationshipName
        {
            get
            {
                return (string)base.GetValue(RelationshipNameProperty);
            }
            set
            {
                base.SetValue(RelationshipNameProperty, value);
            }
        }

        /// <summary>
        /// Output Parameter: True if operation was successful. If False, Error Message will be populated.
        /// </summary>
        public static DependencyProperty SuccessProperty = DependencyProperty.Register("Success", typeof(CrmBoolean), typeof(DistributeWFActivityOnetoMany));
        [CrmOutput("Success")]
        public override CrmBoolean Success
        {
            get
            {
                return (CrmBoolean)base.GetValue(SuccessProperty);
            }
            set
            {
                base.SetValue(SuccessProperty, value);
            }
        }

        /// <summary>
        /// Output Parameter: Error Message is only populated when Success == false.
        /// </summary>
        public static DependencyProperty ErrorMsgProperty = DependencyProperty.Register("ErrorMsg", typeof(string), typeof(DistributeWFActivityOnetoMany));
        [CrmOutput("Error Message")]
        public override string ErrorMsg
        {
            get
            {
                return (string)base.GetValue(ErrorMsgProperty);
            }
            set
            {
                base.SetValue(ErrorMsgProperty, value);
            }
        }

        #endregion

        #region SequenceActivity

        protected override CrmBoolean ExecuteBody(Context context, out string errorMsg)
        {
            errorMsg = null;

            if (this.Workflow == null)
            {
                throw new ArgumentNullException("Workflow");
            }
            this.Distribute(context, this.GatherKeys(context), this.Workflow.Value);

            return new CrmBoolean(true);
        }

        #endregion

        #region Local Methods

        private OneToManyMetadata GetRelationship(Context context)
        {
            RetrieveRelationshipRequest relationshipRequest = new RetrieveRelationshipRequest();
            relationshipRequest.Name = this.RelationshipName;
            relationshipRequest.RetrieveAsIfPublished = false;

            RetrieveRelationshipResponse relationshipResponse = (RetrieveRelationshipResponse)context.SvcMetadataHandle.Execute(relationshipRequest);
            if (relationshipResponse.RelationshipMetadata.RelationshipType != EntityRelationshipType.OneToMany)
            {
                throw new ArgumentException("Relationship is not One to Many", "Relationship Name");
            }

            return (OneToManyMetadata)relationshipResponse.RelationshipMetadata;
        }

        private List<KeyProperty> GatherKeys(Context context)
        {
            OneToManyMetadata relationship = this.GetRelationship(context);

            QueryByAttribute query = new QueryByAttribute();
            query.EntityName = relationship.ReferencingEntity;
            query.Attributes = new string[] { relationship.ReferencingAttribute };
            query.Values = new string[] { context.CurrentRecordId.ToString() };

            RetrieveMultipleRequest retrieveRequest = new RetrieveMultipleRequest();
            retrieveRequest.Query = query;
            retrieveRequest.ReturnDynamicEntities = true;

            // Foreach object just get the primary key
            List<KeyProperty> keyList = new List<KeyProperty>();
            RetrieveMultipleResponse retrieveResponse = (RetrieveMultipleResponse)context.SvcHandle.Execute(retrieveRequest);
            foreach (BusinessEntity be in retrieveResponse.BusinessEntityCollection.BusinessEntities)
            {
                DynamicEntity de = (DynamicEntity)be;
                foreach (Property property in de.Properties)
                {
                    KeyProperty key = (KeyProperty)property;
                    keyList.Add(key);
                    break;
                }
            }

            return keyList;
        }

        private void Distribute(Context context, List<KeyProperty> keyList, Guid workflowId)
        {
            foreach (KeyProperty key in keyList)
            {
                ExecuteWorkflowRequest workflowRequest = new ExecuteWorkflowRequest();
                workflowRequest.EntityId = key.Value.Value;
                workflowRequest.WorkflowId = workflowId;
                context.SvcHandle.Execute(workflowRequest);
            }
        }

        #endregion

    }
}
